package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.exception.BizExceptionUtil;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.security.core.util.SecurityUtils;
import com.dimples.dd.system.convert.NoticeConverter;
import com.dimples.dd.system.enums.notice.NoticePublishStatusEnum;
import com.dimples.dd.system.enums.notice.NoticeReadStatusEnum;
import com.dimples.dd.system.enums.notice.NoticeTargetEnum;
import com.dimples.dd.system.mapper.SystemNoticeMapper;
import com.dimples.dd.system.model.bo.NoticeBO;
import com.dimples.dd.system.model.dto.NoticeDTO;
import com.dimples.dd.system.model.entity.SystemNoticeDO;
import com.dimples.dd.system.model.entity.SystemUserNoticeDO;
import com.dimples.dd.system.model.entity.SystemUsersDO;
import com.dimples.dd.system.model.form.NoticeForm;
import com.dimples.dd.system.model.query.NoticePageQuery;
import com.dimples.dd.system.model.vo.NoticeDetailVO;
import com.dimples.dd.system.model.vo.NoticePageVO;
import com.dimples.dd.system.model.vo.UserNoticePageVO;
import com.dimples.dd.system.service.SystemNoticeService;
import com.dimples.dd.system.service.SystemUserNoticeService;
import com.dimples.dd.system.service.SystemUsersService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.ErrorCode.*;

@Service
@RequiredArgsConstructor
public class SystemNoticeServiceImpl extends ServiceImpl<SystemNoticeMapper, SystemNoticeDO> implements SystemNoticeService {

    private final NoticeConverter noticeConverter;
    private final SystemUserNoticeService systemUserNoticeService;
    private final SystemUsersService systemUserService;

    private final SimpMessagingTemplate messagingTemplate;

    @Override
    public PageResult<NoticePageVO> selectNoticePage(NoticePageQuery queryParams) {
        PageResult<NoticeBO> pageResult = this.baseMapper.selectNoticePage(queryParams);
        return this.noticeConverter.boPage2Vo(pageResult);
    }

    @Override
    public NoticeForm selectNoticeFormData(Long id) {
        return this.noticeConverter.entity2Form(this.baseMapper.selectById(id));
    }

    @Override
    public boolean createNotice(NoticeForm formData) {
        if (NoticeTargetEnum.SPECIFIED.getValue().equals(formData.getTargetType())) {
            List<String> targetUserIdList = formData.getTargetUserIds();
            if (CollectionUtil.isEmpty(targetUserIdList)) {
                throw BizExceptionUtil.exception(NOTICE_PUSH_USER_NOT_NULL);
            }
        }
        SystemNoticeDO entity = this.noticeConverter.form2Entity(formData);
        return this.save(entity);
    }

    @Override
    public boolean updateNotice(Long id, NoticeForm formData) {
        if (NoticeTargetEnum.SPECIFIED.getValue().equals(formData.getTargetType())) {
            List<String> targetUserIdList = formData.getTargetUserIds();
            if (CollectionUtil.isEmpty(targetUserIdList)) {
                throw BizExceptionUtil.exception(NOTICE_PUSH_USER_NOT_NULL);
            }
        }
        return this.updateById(this.noticeConverter.form2Entity(formData));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteNotices(String ids) {
        List<Long> idList = Arrays.stream(ids.split(StrUtil.COMMA))
                .map(Long::parseLong)
                .toList();
        boolean isRemoved = this.removeByIds(idList);
        if (isRemoved) {
            // 删除通知公告的同时，需要删除通知公告对应的用户通知状态
            this.systemUserNoticeService.remove(new LambdaQueryWrapper<SystemUserNoticeDO>().in(SystemUserNoticeDO::getNoticeId, idList));
        }
        return isRemoved;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean publishNotice(Long id) {
        SystemNoticeDO notice = this.getById(id);
        if (notice == null) {
            throw BizExceptionUtil.exception(NOTICE_NOT_FOUND);
        }

        if (NoticePublishStatusEnum.PUBLISHED.getValue().equals(notice.getPublishStatus())) {
            throw BizExceptionUtil.exception(NOTICE_PUBLISHED);
        }

        Integer targetType = notice.getTargetType();
        String targetUserIds = notice.getTargetUserIds();
        if (NoticeTargetEnum.SPECIFIED.getValue().equals(targetType) && StrUtil.isBlank(targetUserIds)) {
            throw BizExceptionUtil.exception(NOTICE_PUSH_USER_NOT_NULL);
        }

        notice.setPublishStatus(NoticePublishStatusEnum.PUBLISHED.getValue());
        notice.setPublisherId(SecurityUtils.getUserId());
        notice.setPublishTime(LocalDateTime.now());
        boolean publishResult = this.updateById(notice);

        if (publishResult) {
            // 发布通知公告的同时，删除该通告之前的用户通知数据，因为可能是重新发布
            this.systemUserNoticeService.remove(new LambdaQueryWrapper<SystemUserNoticeDO>().eq(SystemUserNoticeDO::getNoticeId, id));

            // 添加新的用户通知数据
            List<String> targetUserIdList = null;
            if (NoticeTargetEnum.SPECIFIED.getValue().equals(targetType)) {
                targetUserIdList = Arrays.asList(targetUserIds.split(StrUtil.COMMA));
            }

            List<SystemUsersDO> targetUserList = this.systemUserService.list(
                    new LambdaQueryWrapper<SystemUsersDO>()
                            // 如果是指定用户，则筛选出指定用户
                            .in(
                                    NoticeTargetEnum.SPECIFIED.getValue().equals(targetType),
                                    SystemUsersDO::getId,
                                    targetUserIdList
                            )
            );

            List<SystemUserNoticeDO> userNoticeList = targetUserList.stream().map(user -> {
                SystemUserNoticeDO userNotice = new SystemUserNoticeDO();
                userNotice.setNoticeId(id);
                userNotice.setUserId(user.getId());
                userNotice.setIsRead(NoticeReadStatusEnum.UN_READ.getStatus());
                return userNotice;
            }).toList();

            if (CollectionUtil.isNotEmpty(userNoticeList)) {
                this.systemUserNoticeService.saveBatch(userNoticeList);
            }

            Set<String> receivers = targetUserList.stream().map(SystemUsersDO::getUsername).collect(Collectors.toSet());

            Set<String> allOnlineUsers = this.systemUserService.getAllOnlineUsers();

            // 找出在线用户的通知接收者
            Set<String> onlineReceivers = new HashSet<>(CollectionUtil.intersection(receivers, allOnlineUsers));

            NoticeDTO noticeDTO = new NoticeDTO();
            noticeDTO.setId(id);
            noticeDTO.setTitle(notice.getTitle());
            noticeDTO.setType(notice.getType());
            noticeDTO.setPublishTime(notice.getPublishTime());

            onlineReceivers.forEach(receiver -> messagingTemplate.convertAndSendToUser(receiver, "/queue/message", noticeDTO));
        }
        return publishResult;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean revokeNotice(Long id) {
        SystemNoticeDO notice = this.getById(id);
        if (notice == null) {
            throw BizExceptionUtil.exception(NOTICE_NOT_FOUND);
        }

        if (!NoticePublishStatusEnum.PUBLISHED.getValue().equals(notice.getPublishStatus())) {
            throw BizExceptionUtil.exception(NOTICE_PUBLISHED_OR_REVOKE);
        }

        notice.setPublishStatus(NoticePublishStatusEnum.REVOKED.getValue());
        notice.setRevokeTime(LocalDateTime.now());

        boolean revokeResult = this.updateById(notice);

        if (revokeResult) {
            // 撤回通知公告的同时，需要删除通知公告对应的用户通知状态
            this.systemUserNoticeService.remove(new LambdaQueryWrapper<SystemUserNoticeDO>().eq(SystemUserNoticeDO::getNoticeId, id)
            );
        }
        return revokeResult;
    }

    @Override
    public NoticeDetailVO selectNoticeDetail(Long id) {
        NoticeBO noticeBO = this.baseMapper.selectNoticeDetail(id);
        // 更新用户通知公告的阅读状态
        this.systemUserNoticeService.update(new LambdaUpdateWrapper<SystemUserNoticeDO>()
                .eq(SystemUserNoticeDO::getNoticeId, id)
                .eq(SystemUserNoticeDO::getUserId, SecurityUtils.getUserId())
                .eq(SystemUserNoticeDO::getIsRead, NoticeReadStatusEnum.UN_READ.getStatus())
                .set(SystemUserNoticeDO::getIsRead, NoticeReadStatusEnum.IS_READ.getStatus())
        );
        return noticeConverter.bo2DetailVO(noticeBO);
    }

    @Override
    public PageResult<UserNoticePageVO> selectMyNoticePage(NoticePageQuery queryParams) {
        queryParams.setUserId(SecurityUtils.getUserId());
        return this.systemUserNoticeService.selectMyNoticePage(queryParams);
    }
}
